package com.bigknow.appstore.web.console.service;

import com.bigknow.appstore.proxy.entity.ProxyLog;
import com.bigknow.appstore.proxy.enumerate.CallState;
import com.bigknow.appstore.web.base.entity.User;
import com.bigknow.appstore.web.base.vo.FancyTreeVo;
import com.bigknow.appstore.web.console.app.Application;
import com.bigknow.appstore.web.console.app.ApplicationState;
import com.bigknow.appstore.web.framework.util.AuthenticateUserUtil;
import com.bigknow.net.minidev.json.JSONArray;
import com.bigknow.net.minidev.json.JSONObject;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.query.Query;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import static com.bigknow.appstore.web.framework.util.AuthenticateUserUtil.isSysAdmin;

/**
 * Created by chaos on 2016/10/12.
 */
@Service
public class AppStatisticsService {

    @Autowired
    private Datastore ds;

    /**
     * 返回应用发布状态饼图数据
     * 根据当前用户来判断
     *
     * @return
     */
    public String getAppStatePieData() {
        User user = AuthenticateUserUtil.getUser();
        //不是管理员就加条件，是管理员就看全部
        List<Application> apps = new ArrayList<>();
        if (isSysAdmin(user)) {
            apps = ds.find(Application.class).retrievedFields(true, "state").asList();
        } else {
            apps = ds.find(Application.class).retrievedFields(true, "state").field("userId").equal(user.getId()).asList();
        }
        Map<ApplicationState, List<Application>> map = apps.stream().collect(Collectors.groupingBy(app -> app.getState()));
        JSONArray data = new JSONArray();

        for (ApplicationState state : getStateList()) {
            JSONObject dd = new JSONObject();
            dd.put("name", getName(state));
            if (map.get(state) != null)
                dd.put("value", map.get(state).size());
            else
                dd.put("value", 0);
            data.add(dd);
        }
        return data.toJSONString();
    }

    private String getName(ApplicationState state) {
        switch (state) {
            case TEST:
                return "测试阶段";
            case APPLYING:
                return "申请发布中";
            case PUBLISHED:
                return "已发布";
            case DEPRECATED:
                return "已弃用";
            default:
                return "未知状态";
        }
    }

    private List<ApplicationState> getStateList() {
        List<ApplicationState> list = new ArrayList<>();
        list.add(ApplicationState.TEST);
        list.add(ApplicationState.APPLYING);
        list.add(ApplicationState.PUBLISHED);
        list.add(ApplicationState.DEPRECATED);
        return list;
    }

    /**
     * 返回饼图的标题数据
     *
     * @return
     */
    public String getAppStatePieTitle() {
        JSONArray js = new JSONArray();
        getStateList().forEach(state -> {
            js.add(getName(state));
        });
        return js.toJSONString();
    }

    /**
     * 返回当前用户创建的所有应用，如果是admin就返回所有
     *
     * @return
     */
    public List<Application> getAppsByCurrentUser(String state) {
        User user = AuthenticateUserUtil.getUser();
        ApplicationState ast = getStateByStr(state);
        Query<Application> query = ds.find(Application.class);
        if (ast != null) {
            query.field("state").equal(ast);
        }
        if (isSysAdmin(user)) {
            return query.asList();
        } else {
            return query.field("userId").equal(user.getId()).asList();
        }
    }

    /**
     * 返回当前用户创建的所有应用，如果是admin就返回所有
     *
     * @return
     */
    public List<Application> getAppsByCurrentUser() {
        return getAppsByCurrentUser(null);
    }

    private ApplicationState getStateByStr(String state) {
        switch (state) {
            case "TEST":
                return ApplicationState.TEST;
            case "APPLYING":
                return ApplicationState.APPLYING;
            case "DEPRECATED":
                return ApplicationState.DEPRECATED;
            case "PUBLISHED":
                return ApplicationState.PUBLISHED;
            default:
                return null;
        }
    }

    /**
     * 应用使用整体概况数据
     *
     * @param id
     * @return
     */
    public String appCalledOverview(String id) {
        User user = AuthenticateUserUtil.getUser();
        List<ProxyLog> logs = null;
        Query<ProxyLog> query = ds.createQuery(ProxyLog.class).retrievedFields(true, "callCostTime", "dataSize", "logType");
        //如果不是管理员就只看自己创建的服务
        if (!isSysAdmin(user)) {
            query.filter("proxyResId in", getAppIdList(user.getId()));
        }

        if ("all".equals(id)) {
            logs = query.asList();
        } else {
            logs = query.field("proxyResId").equal(id).retrievedFields(true, "callCostTime", "dataSize", "logType").asList();
        }

        int maxCostTime = 0;
        int minCostTime = 0;
        int avgCostTime = 0;

        long maxDataSize = 0;
        long minDataSize = 0;
        long avgDataSize = 0;

        long successTimes = 0;
        long failureTimes = 0;
        String successRate = "";

        if (logs.size() != 0) {

            //单位ms
            maxCostTime = logs.stream().max((a, b) -> a.getCallCostTime() - b.getCallCostTime()).get().getCallCostTime();
            minCostTime = logs.stream().min((a, b) -> a.getCallCostTime() - b.getCallCostTime()).get().getCallCostTime();
            avgCostTime = (int) (logs.stream().mapToLong(log -> log.getCallCostTime()).reduce((x, y) -> x + y).getAsLong() / logs.size());

            //单位Kb
            maxDataSize = logs.stream().max((a, b) -> a.getDataSize() > b.getDataSize() ? 1 : -1).get().getDataSize() / 1024;
            minDataSize = logs.stream().min((a, b) -> a.getDataSize() > b.getDataSize() ? 1 : -1).get().getDataSize() / 1024;
            avgDataSize = (logs.stream().mapToLong(log -> log.getDataSize()).reduce((x, y) -> x + y).getAsLong() / logs.size()) / 1024;

            successTimes = logs.stream().filter(log -> CallState.SUCCESS.equals(log.getLogType())).count();
            failureTimes = logs.stream().filter(log -> CallState.ERROR.equals(log.getLogType())).count();
            if (failureTimes == 0) {
                successRate = "100";
            } else {
                successRate = new BigDecimal((Float.parseFloat(successTimes + "")
                        / (Float.parseFloat(successTimes + "") + failureTimes)) * 100)
                        .setScale(2, BigDecimal.ROUND_HALF_UP).floatValue() + "";
            }
        }

        JSONObject js = new JSONObject();
        js.put("maxCostTime", maxCostTime);
        js.put("minCostTime", minCostTime);
        js.put("avgCostTime", avgCostTime);

        js.put("maxDataSize", maxDataSize);
        js.put("minDataSize", minDataSize);
        js.put("avgDataSize", avgDataSize);

        js.put("successTimes", successTimes);
        js.put("failureTimes", failureTimes);
        js.put("successRate", successRate);
        return js.toJSONString();
    }

    /**
     * 应用总体使用柱状图数据
     * json:{
     * title:jsonArray
     * success:jsonArray[jsonObject-->jsonObject]
     * fail:jsonArray[jsonObject-->jsonObject]
     * }
     *
     * @return
     */
    public String callTimesTotalBarData() {
        JSONObject js = new JSONObject();
        User user = AuthenticateUserUtil.getUser();
        List<ProxyLog> logs = null;
        Query<ProxyLog> query = ds.createQuery(ProxyLog.class).retrievedFields(true, "proxyResId", "logType");
        query.field("proxyResId").exists();


        //不是admin就只看自己发布的应用的调用情况
        if (!isSysAdmin(user)) {
            query.filter("proxyResId in", getAppIdList(user.getId()));
        }
        logs = query.asList();
        Map<String, List<ProxyLog>> temp = logs.stream().collect(Collectors.groupingBy(log -> log.getProxyResId()));
        List<List<ProxyLog>> list = temp.values().stream().sorted((x, y) -> x.size() - y.size()).collect(Collectors.toList());

        Map<String, String> appNames = getAppNames();
        //title
        JSONArray title = new JSONArray();

        for (List<ProxyLog> proxyLogs : list) {
            title.add(appNames.get(proxyLogs.get(0).getProxyResId()));
        }
        js.put("title", title);

        //success
        JSONArray data = new JSONArray();
        for (List<ProxyLog> proxyLogs : list) {
            JSONObject value = new JSONObject();
            value.put("value", proxyLogs.stream().filter(log -> log.getLogType().equals(CallState.SUCCESS)).count());
            JSONObject item = new JSONObject();
            JSONObject normal = new JSONObject();
            normal.put("color", "#0d9dff");
            item.put("normal", normal);
            value.put("itemStyle", item);
            data.add(value);
        }

        js.put("success", data);

        //fail
        JSONArray fail = new JSONArray();
        for (List<ProxyLog> proxyLogs : list) {
            JSONObject value = new JSONObject();
            value.put("value", proxyLogs.stream().filter(log -> log.getLogType().equals(CallState.ERROR)).count());
            JSONObject item = new JSONObject();
            JSONObject normal = new JSONObject();
            normal.put("color", "#c82a20");
            item.put("normal", normal);
            value.put("itemStyle", item);
            fail.add(value);
        }
        js.put("fail", fail);
        return js.toJSONString();
    }

    private List<String> getAppIdList(String id) {
        List<String> list = ds.find(Application.class).retrievedFields(true, "id").field("userId").equal(id).asList()
                .stream().map(app -> app.getId()).collect(Collectors.toList());
        List<String> notNullValueList = new ArrayList<>();
        notNullValueList.add("nullValue");
        return list == null||list.size()==0 ? notNullValueList : list;
    }

    private Map<String, String> getAppNames() {
        List<Application> list = ds.find(Application.class).retrievedFields(true, "id", "name").asList();
        Map<String, List<Application>> map = list.stream().collect(Collectors.groupingBy(app -> app.getId()));
        Map<String, String> rem = new HashMap<>();
        for (String key : map.keySet()) {
            rem.put(key, map.get(key).get(0).getName());
        }
        return rem;
    }

    /**
     * 应用树
     *
     * @return
     */
    public List<FancyTreeVo> appTree() {
        List<FancyTreeVo> tree = new ArrayList<>();
        User user = AuthenticateUserUtil.getUser();
        Query<Application> query = ds.find(Application.class).retrievedFields(true, "id", "name");
        //如果不是管理员就只看自己创建的应用
        if (!isSysAdmin(user)) {
            query.field("userId").equal(user.getId());
        }
        List<Application> list = query.asList();
        FancyTreeVo root = new FancyTreeVo("1", "应用树");
        root.setExpanded(true);
        root.setSelected(true);
        for (Application app : list) {
            FancyTreeVo node = new FancyTreeVo(app.getId(), app.getName());
            root.getChildren().add(node);
        }
        tree.add(root);
        return tree;
    }

    /**
     * 单个应用线图数据
     *
     * @param id
     * @return
     */
    public String appCallTimesLineData(String id) {
        JSONObject js = new JSONObject();
        JSONArray timeArray = new JSONArray();
        JSONArray dataArray = new JSONArray();
        js.put("time", timeArray);
        js.put("data", dataArray);

        List<ProxyLog> logs = ds.find(ProxyLog.class).field("proxyResId").equal(id)
                .retrievedFields(true, "proxyResId", "occurTime").asList();
        if (logs.size() == 0) {
            return js.toJSONString();
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        logs.stream().forEach(log -> {
            log.setTempTime(format.format(log.getOccurTime()));
        });
        Map<String, List<ProxyLog>> timeMap = logs.stream().collect(Collectors.groupingBy(log -> log.getTempTime()));
        LocalDate lastDate = LocalDate.parse(timeMap.keySet().stream().max((a, b) -> a.compareTo(b)).get(), df)
                .plusDays(2);
        LocalDate startDate = LocalDate.parse(timeMap.keySet().stream().min((a, b) -> a.compareTo(b)).get(), df)
                .plusDays(-8);
        List<String> timeData = new ArrayList<>();
        for (int i = 1; i < lastDate.toEpochDay() - startDate.toEpochDay(); i++) {
            timeData.add(startDate.plusDays(i).format(df));
        }
        timeData.add(lastDate.format(df));
        for (String s : timeData) {
            timeArray.add(s);
        }
        for (String s : timeData) {
            dataArray.add(timeMap.get(s) == null ? 0 : timeMap.get(s).size());
        }
        return js.toJSONString();
    }

    /**
     * 单个应用数据量趋势图
     *
     * @param id
     * @return
     */
    public String appCallDataSizeLineData(String id) {
        JSONObject js = new JSONObject();
        JSONArray timeArray = new JSONArray();
        JSONArray dataArray = new JSONArray();
        js.put("time", timeArray);
        js.put("data", dataArray);

        List<ProxyLog> logs = ds.find(ProxyLog.class).field("proxyResId").equal(id)
                .retrievedFields(true, "proxyResId", "occurTime", "dataSize").asList();
        if (logs.size() == 0) {
            return js.toJSONString();
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd");
        DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        logs.stream().forEach(log -> {
            log.setTempTime(format.format(log.getOccurTime()));
        });
        Map<String, List<ProxyLog>> timeMap = logs.stream().collect(Collectors.groupingBy(log -> log.getTempTime()));
        LocalDate lastDate = LocalDate.parse(timeMap.keySet().stream().max((a, b) -> a.compareTo(b)).get(), df)
                .plusDays(2);
        LocalDate startDate = LocalDate.parse(timeMap.keySet().stream().min((a, b) -> a.compareTo(b)).get(), df)
                .plusDays(-8);
        List<String> timeData = new ArrayList<>();
        for (int i = 1; i < lastDate.toEpochDay() - startDate.toEpochDay(); i++) {
            timeData.add(startDate.plusDays(i).format(df));
        }
        timeData.add(lastDate.format(df));
        for (String s : timeData) {
            timeArray.add(s);
        }
        for (String s : timeData) {
            dataArray.add(timeMap.get(s) == null ? 0 : getDataSizeKB(timeMap.get(s)));
        }
        return js.toJSONString();
    }

    private long getDataSizeKB(List<ProxyLog> proxyLogs) {
        long size = 0;
        for (ProxyLog proxyLog : proxyLogs) {
            size += proxyLog.getDataSize();
        }
        return size / 1024;
    }
}
